package com.ken.autocode.tools.generator.core;

import com.ken.autocode.tools.generator.config.ConfigInfo;
import com.ken.autocode.tools.generator.config.EnvironmentConst;
import com.ken.autocode.tools.generator.config.MyConfig;
import com.ken.autocode.tools.generator.executor.MysqlGenerator;
import com.ken.autocode.tools.generator.executor.OracleGenerator;
import com.ken.autocode.tools.generator.util.*;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 代码生生成器核心工具类
 * @author zhuzj
 *
 * @version $id:Gen.java,v 0.1 2015年12月15日 上午10:47:57 luolin Exp $
 */
public class Gen {

    private Configuration cfg;

    /**
     * 初始化，载入存放模板的文件夹
     */
    public void init(String folderPath,String environment) {
        cfg = new Configuration();
        String file =null;
        if(folderPath!=null && !"".equals(folderPath.trim())){
            file = folderPath;
            System.out.println("定义路径:"+file);
        }else{
            // 这里需要使用一个和template同级目录的JAVA类的class去加载template文件夹路径
            file = Gen.class.getResource("../../../../../../").getFile()+"autotpl/";
            file = file.replaceAll("!|！","");
            System.out.println("目录路径:"+file);
        }
        cfg.setDefaultEncoding("UTF-8");

        File tplFile =new File(file+"Entity.tpl");

        boolean flag = folderPath==null || "".equals(folderPath);

        if(!tplFile.exists()){
            if(flag){
                createCopySysFile(file,environment);
            }else{
                throw  new Error("该路径缺少模板文件");
            }
        }

        File tplPath = new File(file);

        if(!tplPath.exists()){
            throw new Error("文件路径不存在");
        }
        try {
            cfg.setDirectoryForTemplateLoading(tplPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    /**
     * 装载实体类模板对象
     * @param tableInfo {@link TableInfo}
     * @param className
     * @param topLevel 顶层包名
     * @param moduleName 模块名称
     * @return {@link EntityTpl}
     * @throws Exception
     */
    public EntityTpl getTblInfo(TableInfo tableInfo, String className, String topLevel, String moduleName,String proName,String version,String title) throws Exception {
        EntityTpl tpl = new EntityTpl();
        tpl.setPkColumn(MyStringUtil.getEntityPK(ConfigInfo.pkColom));// 主键
        tpl.setTablecomment(tableInfo.getComment());// 注释
        tpl.setClassName(className);// 实体类的类名
        tpl.setClassObjectName(tpl.getClassObjectName());
        tpl.setClassMiniName(tpl.getClassName().toLowerCase());  //设置小写类名

        tpl.setTablename(tableInfo.getName());// 数据表名
        tpl.setAuthor(ConfigInfo.AUTHOR);

        //设置一级标题 二级标题
        tpl.setModuleName(moduleName);

        tpl.setTitle(title);
        tpl.setVersion(version);
        tpl.setProName(proName);

        tpl.setEntityPackage(topLevel + "." +moduleName+getRealteRoot(ConfigInfo.Entity_FOLEDR));//entity包名
        tpl.setMapperInterfacePackage(topLevel + "." +moduleName+getRealteRoot(ConfigInfo.DAO_FOLEDR));//mapper包名
        tpl.setMapperXmlPackage(topLevel + "." +moduleName+getRealteRoot(ConfigInfo.DAO_IMPL_FOLEDR));// mapper.xml包名
        tpl.setServicePackage(topLevel + "." +moduleName+getRealteRoot(ConfigInfo.SINFE_FOLEDR));//service包名
        tpl.setServiceImplPackage(topLevel + "." +moduleName+ getRealteRoot(ConfigInfo.SIMPL_FOLDER));//serviceImpl包名
        tpl.setControllerPackage(topLevel + "." +moduleName+ getRealteRoot(ConfigInfo.CONTROLLER_FOLEDR));//controller包名

        tpl.setViewPackage(moduleName+"."+className.toLowerCase());//页面的文件夹路径

        // 循环表的字段，封装Entity对应的字段信息
        for (FieldInfo fi : tableInfo.getFields()) {
            tpl.addRecord(new RecordInfo(fi.getFieldName(), fi.getType(), fi.getJdbcType(), fi.getComment(), fi.getPk()));
        }

        return tpl;
    }


    /***
     * 生成代码
     * @param tables 要自动映射处理的表的集合
     * @param myfile
     * @throws Exception
     */
    public void gen(List<TableInfo> tables, MyFile myfile, String topLevel, String moduleName, String proName, String version, String title) throws Exception {

        for (TableInfo table : tables) {
            String className = MyStringUtil.getEntityName(table.getName(),true);
            // 装载实体类模板对象
            EntityTpl entity = getTblInfo(table,className , topLevel, moduleName,proName,version,title);
            // 获得主键
            entity.getPkRecords();

            //字段数据按三个进行分组
            entity.setGroups(ListGroupUtils.createListGroup(entity.getRecords(),3));
            Map<String, String[]> templatefileMap = ConfigInfo.templatefileMap;

            String[] templateNames =templatefileMap.get("name");

            String[] templateSufix =templatefileMap.get("sufix");

            if(templateNames.length!=templateSufix.length){
                throw  new Error("模板名称和模板后缀个数不一致,请检查");
            }

            String fileName =null;
            for(int iv=0;iv<templateNames.length;iv++){
                fileName = (templateNames[iv].contains("List")||templateNames[iv].contains("list")||templateNames[iv].contains("Other")
                        ||templateNames[iv].contains("Update")||templateNames[iv].contains("update"))?className.toLowerCase():className;
                autoCreateFile(templateNames[iv],templateSufix[iv].replaceAll(".jsp|.jspf|.jspx|.html|.ftl",ConfigInfo.VIEW_SUFFIX)
                        ,fileName,entity,myfile);
            }
        }
    }

    /**
     * 功能描述: 
     * @param templ 模板路径"/模板名称.tpl"
     * @param sufix  针对的模板类型 如controller 等等
     * @param fileName 生成的文件名称 至少与它有关
     * @param entity 模板实体
     * @param myfile 文件信息【包括各层级的根路径信息】
     * @return: void
     * @author: swc
     * @date: 2020/5/25 21:58
    */ 
    private void autoCreateFile(String templ, String sufix, String fileName, EntityTpl entity,MyFile myfile) {
        try {
            Template template = cfg.getTemplate(templ);
            StringWriter writer = new StringWriter();
            template.process(entity, writer);
            //js
            myfile.writeFile(writer.toString(), fileName, sufix,entity);
        }catch (Exception ex){
            ex.printStackTrace();
        }

    }

    /**
     * @descript: set your config
     * @param myConfig
     * @return: void
     * @author: swc
     * @date: 2020/5/18 0018 下午 18:10
     */
    public static void setConfig(MyConfig myConfig){
        Class<MyConfig> aClass = MyConfig.class;
        Field[] declaredFields = aClass.getDeclaredFields();

        Class<ConfigInfo> configInfoClass = ConfigInfo.class;

        for (Field field:declaredFields){

            try {
                Method declaredMethod = aClass.getDeclaredMethod("get" + firstUpperCase(field.getName()));
                declaredMethod.setAccessible(true);

                Object invoke = declaredMethod.invoke(myConfig);

                boolean check = true;
                if(invoke instanceof  String){
                    check =!"".equalsIgnoreCase((String)invoke);
                }

                if(invoke!=null && check){
                    Field conField = configInfoClass.getField(field.getName());
                    conField.setAccessible(true);
                    conField.set(null,invoke);
                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }
    }

    /**
     * 功能描述: 执行的方法
     * @return: void
     * @author: swc
     * @date: 2020/5/22 0022 下午 16:22
     */
    public  void toDo(String[] tableNames) {
        try {
            // 用于建立目录和写文件
            MyFile myfile = new MyFile(ConfigInfo.Entity_FOLEDR,ConfigInfo.SIMPL_FOLDER,
                    ConfigInfo.SINFE_FOLEDR,ConfigInfo.CONTROLLER_FOLEDR,ConfigInfo.TOP_LEVEL,ConfigInfo.MODULE_NAME,ConfigInfo.PRO_SITE);

            // 将需要添加的表存入map中，key为表名，value为生成的实体类的注释
            Map<String, String> map = new HashMap<String, String>();

            String tableComment ="";

            List<TableInfo> tables =null;

            for(String tableName:tableNames){
                //oracle数据库
                if(ConfigInfo.DB_URL.contains("jdbc:oracle")){
                    tableComment =FieldUtils.getOracleTableInfo(tableName,ConfigInfo.DB_URL, ConfigInfo.DB_USERL,ConfigInfo.DB_PWD);
                    map.put(tableName.toLowerCase(),tableComment);
                    tables = OracleGenerator.getTableInfo(map);
                }
                //mysql数据库
                else if(ConfigInfo.DB_URL.contains("jdbc:mysql")){
                    tableComment =FieldUtils.getMysqlTableInfo(tableName,ConfigInfo.DB_URL, ConfigInfo.DB_USERL,ConfigInfo.DB_PWD);
                    map.put(tableName.toLowerCase(),tableComment);
                    tables = MysqlGenerator.getTableInfo(map);
                }
            }

            this.gen(tables, myfile,ConfigInfo.TOP_LEVEL,ConfigInfo.MODULE_NAME,ConfigInfo.PRO_NAME,ConfigInfo.VERSION,ConfigInfo.TITLE);

            System.out.println("=======共计生成："+ tables.size() + "张表"+"========" );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 功能描述: first world upperCase
     * @param str
     * @return: java.lang.String
     * @author: swc
     * @date: 2020/5/26 0026 上午 11:33
    */
    public static String firstUpperCase(String str) {
        char[] chars = str.toCharArray();
        if(chars[0]>='a'  &&  chars[0]<='z'){
            chars[0]-=32;
        }
        return String.valueOf(chars);
    }



    /**
     * 功能描述: 将原有项目的模板文件复制到该项目的target-classes下面
     * @param rootFolder
     * @param environment
     * @return: void
     * @author: swc
     * @date: 2020/5/19 0019 下午 12:52
     */
    private void createCopySysFile(String rootFolder, String environment) {
        if(environment.equals(EnvironmentConst.DEV)){
            return;
        }
        String[] fileNames = ConfigInfo.templatefileMap.get("name");
                //new String[]{"Entity.tpl","Mapper.tpl","MapperXML.tpl",
                //"Service.tpl","ServiceImpl.tpl","Controller.tpl","ViewList.tpl","ViewUpdate.tpl"};
        for(String fileName:fileNames){
            createTemplateFile(rootFolder,fileName);
        }
    }

    public static void createTemplateFile(String rootFolder,String fileName){
        FileOutputStream out = null;
        InputStream is= null;
        try {
            is = Gen.class.getClassLoader().getResourceAsStream(fileName);
            byte bytes [] = new byte [is.available()];
            is.read(bytes);
            File file =new File(rootFolder+fileName);
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }

            out = new FileOutputStream(rootFolder+fileName);
            out.write(bytes);
            out.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }

    public  String getRealteRoot(String folder){
        return "."+folder.replaceAll("[\\\\//]",".");
    }

}
